#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>
#include <functional>
#include "../../Mutex/Mutex.hpp"
#include "../../Log/Log.hpp"

using namespace LogModule;

using func_t = std::function<std::string(const std::string &)>;

const int defaultfd = -1;

class UdpServer
{
public:
    UdpServer(uint16_t port, func_t func) // 构造函数，传入端口号和回调函数
        : _sockfd(defaultfd), _port(port), _isrunning(false), _func(func)
    {
    }
    ~UdpServer()
    {
    }

    void Init()
    {
        // 1. 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create socket failed";
            exit(1);
        }
        LOG(LogLevel::INFO) << "create socket success, sockfd = " << _sockfd;
        // 2. 绑定socket信息，主要是ip和端口，ip(比较特殊，我们后续解释)
        // 2.1 填充sockaddr_in结构体
        struct sockaddr_in local;
        // memset(&local, 0, sizeof(local)); // 防止干扰
        bzero(&local, sizeof(local)); // 防止干扰
        local.sin_family = AF_INET;
        // 我会不会把我的IP地址和端口号发送给对方？
        // 肯定会的！也就决定了IP信息和端口信息一定要发送到网络上
        // 我们就需要将本地存储序列格式转换成网络序列格式，因为网络字节序统一是大端
        local.sin_port = htons(_port);
        // IP同上，但是我们的IP是点分十进制的string风格，需要将IP转为4字节的网络字节序
        // inet_addr()函数就是用来将点分十进制的IP地址转为4字节网络字节序的,小端转大端是比较容易的，我们来说说IP转4字节网络字节序的过程
        // 我们了一定义一个结构体ip，内容是char part1/2/3/4（地址从低到高），对于"188.168.1.1"，以点为分隔符，拆成4个子字符串，在转化成数字，然后将数字转化成4字节网络字节序
        // s_addr成员变量就是用来存储4字节网络字节序的
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 还有别的做法

        local.sin_addr.s_addr = INADDR_ANY; // 这里用的是INADDR_ANY，表示接收所有IP地址，这样可以接收来自任何IP的消息
        int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind socket failed";
            exit(2);
        }
        LOG(LogLevel::INFO) << "bind socket success, port = " << _port;
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 1.收消息
            int s = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (s > 0)
            {
                int peer_port = ntohs(peer.sin_port);           // 网络字节序转主机字节序，获取对方端口号
                std::string peer_ip = inet_ntoa(peer.sin_addr); // 网络字节序转主机字节序，获取对方IP地址
                LOG(LogLevel::DEBUG) << "recvfrom peer_ip = " << peer_ip << ", peer_port = " << peer_port;

                buffer[s] = '\0';

                std::string result = _func(buffer); // 调用回调函数进行处理----分层处理

                // LOG(LogLevel::DEBUG) << "buffer = " << buffer; // 1.消息内容 2.谁发的？
                //  2.发消息
                // std::string echo_string = "server say@ ";
                // echo_string += buffer;
                // sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr *)&peer, len);
                sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr *)&peer, len);
            }
        }
    }

private:
    int _sockfd;
    uint16_t _port;
    // std::string _ip; // 用的是字符串风格，点分十进制的IP地址
    bool _isrunning;
    func_t _func; // 服务器的回调函数，用来进行对数据进行处理
};

// using namespace LogModule;

// const int defaultfd = -1;

// class UdpServer {
// public:
//     UdpServer(uint16_t port)
//     :_port(port), _sockfd(defaultfd),_isrunning(false) 
//     {}
    
//     ~UdpServer(){}

//     void Init()
//     {
//         // 1. 创建套接字
//         _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//         if(_sockfd < 0) {
//             LOG(LogLevel::FATAL) << "create socket failed!";
//             exit(1);
//         }
//         LOG(LogLevel::INFO) << "create socket success, sockedfd = " << _sockfd;
//         // 2. 绑定IP和端口
//         // 2.1 填充sockaddr_in字段
//         struct sockaddr_in local;
//         bzero(&local, sizeof(local));
//         local.sin_family = AF_INET;
//         local.sin_port = htons(_port);
//         // local.sin_addr.s_addr = inet_addr(_ip.c_str());
//         local.sin_addr.s_addr = INADDR_ANY;
//         int n = bind(_sockfd, (struct sockaddr*)&local, sizeof local);
//         if(n < 0) {
//             LOG(LogLevel::FATAL) << "bind socket failed";
//             exit(2);
//         }
//         LOG(LogLevel::INFO) << "bind socket sucess, port = " << _port;
//     }

//     void Start() {
//         _isrunning = true;
//         while(_isrunning) {
//             char buffer[1024];
//             struct sockaddr_in peer;
//             socklen_t len = sizeof(peer);
//             // 1. 收消息
//             int s = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&peer, &len);
//             if(s > 0) {
//                 buffer[s] = 0;
//                 LOG(LogLevel::DEBUG) << "buffer = " << buffer;
//                 // 2. 发送消息
//                 std::string echo_string = "server say#";
//                 echo_string += buffer;
//                 sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&peer, len);
//             }
//         }
//     }
// private:
//     int _sockfd;
//     uint64_t _port;
//     // std::string _ip;
//     bool _isrunning;
// };